package com.xmh.utils.automaticGeneration;

import java.io.BufferedWriter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.xmh.utils.FileUtil;

/**
 * 代码自动生成
 * 
 * @author
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring_bean.xml")
@Transactional
public class AutomaticCodeGenerationBySSM {
	/*
	 * -==============-=-=================-==============-==========================
	 * -============== =====================控制台==================================
	 * -==============-=-=================-==============-==========================
	 */
	private final String vo_Path = "F:\\eclipseWord\\skyCash\\src\\com\\out\\entity\\";

	private final String controller_Path = "F:\\eclipseWord\\skyCash\\src\\com\\out\\controller\\system\\";

	private final String service_Path = "F:\\eclipseWord\\skyCash\\src\\com\\out\\service\\";

	private final String dao_Path = "F:\\eclipseWord\\skyCash\\src\\com\\out\\dao\\";

	private final String xml_Path = "F:\\eclipseWord\\skyCash\\resources\\mybatis\\system\\";

	private final String[] table_Name_List = { "t_video" };
	/*
	 * -==============-=-=================-==============-==========================
	 * -============== =============================================================
	 * -==============-=-=================-==============-==========================
	 */

	@Resource(name = "dataSource")
	private ComboPooledDataSource comboPooledDataSource;

	private final String controle_Code = FileUtil
			.getFileStr(this.getClass().getResource("/model/SSM/controleModel.model").getPath());

	private final String service_Impl_Code = FileUtil
			.getFileStr(this.getClass().getResource("/model/SSM/serviceImplementsModel.model").getPath());

	private final String service_Inte_Code = FileUtil
			.getFileStr(this.getClass().getResource("/model/SSM/serviceInterfaceModel.model").getPath());

	private final String dao_Inte_Code = FileUtil
			.getFileStr(this.getClass().getResource("/model/SSM/daoInterface.model").getPath());

	private final String dao_Impl_Code = FileUtil
			.getFileStr(this.getClass().getResource("/model/SSM/daoInterfaceImpl.model").getPath());

	@Test
	public void create() throws Exception {
		List<Object> tableList = selectTalbeDetailed(table_Name_List);

		createVo(tableList);
		createXml(tableList);
		createMode(tableList, dao_Inte_Code, dao_Path, "DAO");
		createMode(tableList, dao_Impl_Code, dao_Path, "DAOImpl");
		createMode(tableList, controle_Code, controller_Path, "Controller");
		createMode(tableList, service_Inte_Code, service_Path, "Service");
		createMode(tableList, service_Impl_Code, service_Path, "ServiceImpl");
	}

	/**
	 * 根据模板文件创建对应的文件以及对应路径
	 * 
	 * @param tableList
	 * @param modeCode
	 * @param path
	 * @param nameSuffix
	 */
	@SuppressWarnings("unchecked")
	private void createMode(List<Object> tableList, String modeCode, String path, String nameSuffix) {
		for (Object object : tableList) {
			Map<String, Object> tableMap = (Map<String, Object>) object;
			// 转为驼峰
			String voName = nameTransformation((String) tableMap.get("tableName"));
			// 首字母大写
			String className = titleCase(voName);

			String filePath = path + voName;
			String fileName = className + nameSuffix;

			filePath += fileName.substring(fileName.length() - 4).equals("Impl") ? "\\impl\\" : "";

			String code = extracted(voName, className, modeCode);

			outFile(code, filePath, fileName, "java");
		}
	}

	/**
	 * 
	 * 替换对应的包名-类型，以及表名
	 * 
	 * @param voName
	 * @param className
	 * @param code
	 * @return
	 */
	private String extracted(String voName, String className, String code) {
		String nowStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());

		String replace = code.replace("______y______", voName);
		replace = replace.replace("_____X______", className);
		replace = replace.replace("____Date____", nowStr);
		replace = replace.replace("___EPath___", getPackagePath(vo_Path));
		replace = replace.replace("___SPath___", getPackagePath(service_Path));
		replace = replace.replace("___DPath___", getPackagePath(dao_Path));
		replace = replace.replace("___CPath___", getPackagePath(controller_Path));

		return replace;
	}

	/**
	 * 根据文件相对路径获取包路径
	 * 
	 * @param path
	 * @return
	 */
	private String getPackagePath(String path) {
		String str = path.split("src")[1];
		String packPach = str.substring(1, str.length() - 1).replace("\\", ".");
		return packPach;
	}

	/**
	 * 创建xml文件
	 * 
	 * @param tableNameMaplist
	 */
	@SuppressWarnings("unchecked")
	private void createXml(List<Object> tableNameMaplist) {
		for (Object object : tableNameMaplist) {
			Map<String, Object> tableMap = (Map<String, Object>) object;

			String tableName = (String) tableMap.get("tableName");
			// 转为驼峰
			String voName = nameTransformation(tableName);
			// 首字母大写
			String className = titleCase(voName);

			StringBuffer asColumn = new StringBuffer();
			StringBuffer ifLabel = new StringBuffer();
			StringBuffer addColumnUp = new StringBuffer();
			StringBuffer addColumnDown = new StringBuffer();
			StringBuffer updateSetLabelSb = new StringBuffer();

			List<Object> list = (List<Object>) tableMap.get("columnList");
			for (int i = 0; i < list.size(); i++) {
				Map<String, Object> columnMap = (Map<String, Object>) list.get(i);

				String columnName = (String) columnMap.get("columnName");

				String type = (String) columnMap.get("type");

				// 字段名转为驼峰
				String cName = nameTransformation(columnName);

				if (i == list.size() - 1) {
					asColumn.append("			" + columnName + " AS " + cName + "\n");
					addColumnUp.append("			" + columnName + ")\n");
					addColumnUp.append("		VALUES ( \n");
					addColumnDown.append("			#{" + cName + "})\n");
				} else {
					asColumn.append("			" + columnName + " AS " + cName + ",\n");
					addColumnUp.append("			" + columnName + ",\n");
					addColumnDown.append("			#{" + cName + "},\n");
				}

				String ifStartBelan = null;
				if ("VARCHAR".equals(type)) {
					ifStartBelan = "			<if test=\"" + cName + " != null and " + cName + " != '' \">\n";
				} else {
					ifStartBelan = "			<if test=\"" + cName + " != null \">\n";
				}
				String andString = "				and " + columnName + " = #{" + cName + "} \n";
				String ifEndBelan = "			</if>\n";

				ifLabel.append(ifStartBelan);
				ifLabel.append(andString);
				ifLabel.append(ifEndBelan);

				if (!columnName.equals("id")) {
					updateSetLabelSb.append(ifStartBelan);
					updateSetLabelSb.append("				" + columnName + " = #{" + cName + "},  \n");
					updateSetLabelSb.append(ifEndBelan);
				}
			}
			StringBuffer xmlFileSb = new StringBuffer();

			xmlFileSb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
			xmlFileSb.append("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n");
			xmlFileSb.append("		\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n");
			xmlFileSb.append("<mapper namespace=\"_____X______XML\">\n");
			xmlFileSb.append("\n");
			xmlFileSb.append(
					"	<select id=\"get_____X______\" parameterType=\"___EPath___.______y______._____X______Entity\" resultType=\"___EPath___.______y______._____X______Entity\">\n");
			xmlFileSb.append("		SELECT \n");
			xmlFileSb.append(asColumn);
			xmlFileSb.append("		FROM _____TableName______ \n");
			xmlFileSb.append("		<where>\n");
			xmlFileSb.append(ifLabel);
			xmlFileSb.append("		</where>\n");
			xmlFileSb.append("	</select>\n\n");
			xmlFileSb.append(
					"	<insert id=\"add_____X______\"  parameterType=\"___EPath___.______y______._____X______Entity\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n");
			xmlFileSb.append("		INSERT INTO _____TableName______ (\n");
			xmlFileSb.append(addColumnUp);
			xmlFileSb.append(addColumnDown);
			xmlFileSb.append("	</insert>\n\n");
			xmlFileSb.append(
					"	<delete id=\"delete_____X______\" parameterType=\"___EPath___.______y______._____X______Entity\">\n");
			xmlFileSb.append("		DELETE FROM _____TableName______\n");
			xmlFileSb.append("		<where>\n");
			xmlFileSb.append(ifLabel);
			xmlFileSb.append("		</where>\n");
			xmlFileSb.append("	</delete>\n\n");
			xmlFileSb.append(
					"	<update id=\"update_____X______\" parameterType=\"___EPath___.______y______._____X______Entity\">\n");
			xmlFileSb.append("		UPDATE _____TableName______\n");
			xmlFileSb.append("		<set>\n");
			xmlFileSb.append(updateSetLabelSb);
			xmlFileSb.append("		</set>\n");
			xmlFileSb.append("		where id = #{id}\n");
			xmlFileSb.append("	</update>\n\n");
			xmlFileSb.append(
					"	<select id=\"select_____X______\" parameterType=\"java.util.Map\" resultType=\"___EPath___.______y______._____X______Entity\">\n");
			xmlFileSb.append("		SELECT \n");
			xmlFileSb.append(asColumn);
			xmlFileSb.append("		FROM _____TableName______ \n");
			xmlFileSb.append("		<where>\n");
			xmlFileSb.append(ifLabel);
			xmlFileSb.append("		</where>\n");
			xmlFileSb.append("		<if test=\"pageIndex != null \">\n");
			xmlFileSb.append("			<if test=\"pageNumber != null \">\n");
			xmlFileSb.append("				LIMIT #{pageIndex}, #{pageNumber}\n");
			xmlFileSb.append("			</if>\n");
			xmlFileSb.append("		</if>\n");
			xmlFileSb.append("	</select>\n");
			xmlFileSb.append("</mapper>");

			String xmlCode = xmlFileSb.toString();

			xmlCode = extracted(voName, className, xmlCode);

			xmlCode = xmlCode.replace("_____TableName______", tableName);

			outFile(xmlCode, xml_Path, className + "XML", "xml");
		}
	}

	/**
	 * 创建实体类
	 * 
	 * @param tableNameMaplist
	 */
	@SuppressWarnings("unchecked")
	private void createVo(List<Object> tableNameMaplist) {
		for (Object object : tableNameMaplist) {
			Map<String, Object> tableMap = (Map<String, Object>) object;

			String tableName = (String) tableMap.get("tableName");
			// 转为驼峰
			String voName = nameTransformation(tableName);
			// 首字母大写
			String className = titleCase(voName) + "Entity";

			StringBuffer voClassFileSb = new StringBuffer();
			StringBuffer columnSb = new StringBuffer();
			StringBuffer getSetSb = new StringBuffer();
			StringBuffer getVo = new StringBuffer();
			StringBuffer getMap = new StringBuffer();
			StringBuffer isEmptySb = new StringBuffer();

			voClassFileSb.append("package " + getPackagePath(vo_Path) + "." + voName + ";\n\n");
			voClassFileSb.append("import java.util.Map;\n");
			voClassFileSb.append("import java.util.Date;\n");
			voClassFileSb.append("import java.util.HashMap;\n");
			voClassFileSb.append("import java.math.BigDecimal;\n");
			voClassFileSb.append("import java.sql.Timestamp;\n");
			voClassFileSb.append("import com.alibaba.fastjson.JSONObject;\n");
			voClassFileSb.append("import java.io.Serializable;\n\n");
			voClassFileSb.append("public class " + className + " implements Serializable {\n\n");

			getVo.append("	public " + className + "(Map<String, Object> map) {\n");
			getVo.append("		if (map == null || map.isEmpty()) {\n");
			getVo.append("			return;\n");
			getVo.append("		}\n\n");

			getMap.append("\n	public Map<String, Object> toMap(){\n");
			getMap.append("		Map<String, Object> map = new HashMap<String, Object>();\n\n");

			isEmptySb.append("	public boolean isEmpty() {\n");

			List<Object> list = (List<Object>) tableMap.get("columnList");
			for (int i = 0; i < list.size(); i++) {
				Map<String, Object> columnMap = (Map<String, Object>) list.get(i);

				String columnName = (String) columnMap.get("columnName");
				// 字段名转为驼峰
				String cName = nameTransformation(columnName);

				// 字段名转为驼峰后首字母大写
				String titleCaseCname = titleCase(cName);

				// 转为对应的java类型
				String javaType = typeTransformation((String) columnMap.get("type"));

				String remarks = (String) columnMap.get("remarks");

				columnSb.append("	/**\n	 *____remarks____\n	 */\n");
				columnSb.append("	private ____javaType____ ____cName____;\n\n");

				getSetSb.append("	/**\n");
				getSetSb.append("	 * ____remarks____\n");
				getSetSb.append("	 */\n");
				getSetSb.append("	public ____javaType____ get____ClassName____() {\n");
				getSetSb.append("		return ____cName____;\n");
				getSetSb.append("	}\n\n");
				getSetSb.append("	/**\n");
				getSetSb.append("	 * ____remarks____\n");
				getSetSb.append("	 */\n");
				getSetSb.append("	public void set____ClassName____(____javaType____ ____cName____) {\n");
				getSetSb.append("		this.____cName____ = ____cName____;\n");
				getSetSb.append("	}\n\n");

				if ("String".equals(javaType)) {
					getVo.append("		____cName____ = (String) map.get(\"____cName____\");\n\n");
				} else if ("Date".equals(javaType)) {
					getVo.append("		Object ____cName____obj = map.get(\"____cName____\");\n"
							+ "		if (____cName____obj != null) {\n"
							+ "			if (____cName____obj instanceof Date) {\n"
							+ "				____cName____ = (Date) ____cName____obj;\n" + "			} else {\n"
							+ "				____cName____ = new Date((long) ____cName____obj);\n" + "			}\n"
							+ "		}\n\n");
				} else if ("BigDecimal".equals(javaType)) {
					getVo.append("		if (map.get(\"____cName____\") != null) {\n");
					getVo.append(
							"			____cName____ = new BigDecimal(String.valueOf(map.get(\"____cName____\")));\n");
					getVo.append("		}\n\n");
				} else {
					getVo.append("		if (map.get(\"____cName____\") != null) {\n");
					getVo.append(
							"			____cName____ = ____javaType____.valueOf(String.valueOf(map.get(\"____cName____\")));\n");
					getVo.append("		}\n\n");
				}
				getMap.append("		map.put(\"____cName____\", ____cName____);\n");

				isEmptySb.append("		if (____cName____ != null) {\n");
				isEmptySb.append("			return false;\n");
				isEmptySb.append("		}\n");

				isEmptySb = new StringBuffer(isEmptySb.toString().replace("____cName____", cName));
				getMap = new StringBuffer(getMap.toString().replace("____cName____", cName));

				String voStr = getVo.toString();
				voStr = voStr.replace("____cName____", cName);
				voStr = voStr.replace("____javaType____", javaType);
				getVo = new StringBuffer(voStr);

				String columnStr = columnSb.toString();
				columnStr = columnStr.replace("____javaType____", javaType);
				columnStr = columnStr.replace("____cName____", cName);
				columnStr = columnStr.replace("____remarks____", remarks);
				columnSb = new StringBuffer(columnStr);

				String getSetStr = getSetSb.toString();
				getSetStr = getSetStr.replace("____cName____", cName);
				getSetStr = getSetStr.replace("____remarks____", remarks);
				getSetStr = getSetStr.replace("____ClassName____", titleCaseCname);
				getSetStr = getSetStr.replace("____javaType____", javaType);
				getSetSb = new StringBuffer(getSetStr);
			}
			getVo.append("	}\n\n");

			getMap.append("		return map;\n");
			getMap.append("	}\n\n");

			isEmptySb.append("		return true;\n");
			isEmptySb.append("	}\n");

			voClassFileSb.append(columnSb);
			voClassFileSb.append("	@SuppressWarnings(\"unchecked\")\n");
			voClassFileSb.append("	public " + className + "(String json) {\n");
			voClassFileSb.append("		this(JSONObject.parseObject(json, Map.class));\n");
			voClassFileSb.append("	}\n\n");
			voClassFileSb.append(getSetSb);
			voClassFileSb.append("	public " + className + "() {}\n\n");
			voClassFileSb.append(getVo);
			voClassFileSb.append(getMap);
			voClassFileSb.append(isEmptySb);
			voClassFileSb.append("\n}");

			outFile(voClassFileSb.toString(), vo_Path + voName, className, "java");
		}
	}

	private String typeTransformation(String type) {
		if (type.equals("INT") || type.equals("TINYINT") || type.equals("SMALLINT")) {
			return "Integer";
		} else if (type.equals("VARCHAR") || type.equals("TEXT")) {
			return "String";
		} else if (type.equals("BIGINT")) {
			return "Long";
		} else if (type.equals("BIT")) {
			return "Boolean";
		} else if (type.equals("FLOAT")) {
			return "Float";
		} else if (type.equals("DOUBLE")) {
			return "Double";
		} else if (type.equals("TIMESTAMP")) {
			return "Timestamp";
		} else if (type.equals("DECIMAL") || type.equals("DECIMAL UNSIGNED")) {
			return "BigDecimal";
		} else if (type.equals("DATETIME") || type.equals("DATE")) {
			return "Date";
		}
		return type;
	}

	/**
	 * 根据表名获取到表格的详细数据
	 * 
	 * @param tableNames
	 * @return
	 * @throws SQLException
	 */
	private List<Object> selectTalbeDetailed(String... tableNames) throws SQLException {
		List<Object> tableNameMaplist = new ArrayList<Object>();

		// 获得元数据
		DatabaseMetaData metaData = comboPooledDataSource.getConnection().getMetaData();
		// 获得表信息
		ResultSet tables = metaData.getTables(null, null, null, new String[] { "TABLE" });

		while (tables.next()) {
			// 获得表名
			String table_name = tables.getString("TABLE_NAME");

			boolean isCon = false;
			for (String tableName : tableNames) {
				if (tableName.equals(table_name)) {
					isCon = false;
					break;
				} else {
					isCon = true;
				}
			}

			if (isCon) {
				continue;
			}

			// 通过表名获得所有字段名
			ResultSet columns = metaData.getColumns(null, null, table_name, "%");

			List<Object> list = new ArrayList<Object>();

			// 获得所有字段名
			while (columns.next()) {
				Map<String, String> columnNameMap = new HashMap<>(); // 保存字段名
				String column_name = columns.getString("COLUMN_NAME");
				String type_name = columns.getString("TYPE_NAME");
				String remarks = columns.getString("REMARKS");

				columnNameMap.put("type", type_name);
				columnNameMap.put("columnName", column_name);
				columnNameMap.put("remarks", remarks);

				list.add(columnNameMap);
			}
			Map<String, Object> tableNameMap = new HashMap<>();
			tableNameMap.put("tableName", table_name);
			tableNameMap.put("columnList", list);
			tableNameMaplist.add(tableNameMap);
		}
		return tableNameMaplist;
	}

	/**
	 * 输出文件到指定目录文件夹
	 * 
	 * @param code
	 * @param path
	 * @param fileName
	 * @param fileType
	 */
	private void outFile(String code, String path, String fileName, String fileType) {
		try {
			File file = new File(path + "//" + fileName + "." + fileType);

			if (!file.exists()) {
				new File(path).mkdirs();
				file.createNewFile();
			}
			OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
			BufferedWriter writer = new BufferedWriter(write);
			writer.write(code);
			writer.flush();
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private String nameTransformation(String name) {
		if (name.indexOf("T_") == 0 || name.indexOf("t_") == 0) {
			name = name.substring(2, name.length());
		}

		Pattern linePattern = Pattern.compile("_(\\w)");
		name = name.toLowerCase();
		Matcher matcher = linePattern.matcher(name);
		StringBuffer sb = new StringBuffer();
		while (matcher.find()) {
			matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
		}
		matcher.appendTail(sb);
		String string = sb.toString();

		return string;

	}

	/**
	 * 首字母大写
	 * 
	 * @param string
	 * @return
	 */
	private String titleCase(String string) {
		return string.substring(0, 1).toUpperCase() + string.substring(1, string.length());
	}
}
